﻿
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using BlawkayEngine;

namespace BlawkayHockey
{
    class Quad
    {

        public Quad(Game oGame, string oImagePath, Vector3 position, Vector3 origin, Vector3 normal, Vector3 up, float width, float height, bool focusOnCamera = true)
        {
            _Position = position;
            _Direction = Vector3.Right;
            _FocusedOnCamera = focusOnCamera;

            Vertices = new VertexPositionNormalTexture[4];
            Indexes = new short[6];
            Origin = origin;
            Normal = normal;
            Up = up;

            // Calculate the quad corners
            Vector3 uppercenter = (Up * height / 2) + origin;

            Left = Vector3.Cross(normal, Up);
            UpperLeft = uppercenter + (Left * width / 2);
            UpperRight = uppercenter - (Left * width / 2);
            LowerLeft = UpperLeft - (Up * height);
            LowerRight = UpperRight - (Up * height);

            _Effect = new BasicEffect(oGame.GraphicsDevice);

            _Effect.World = Matrix.Identity;
            _Effect.TextureEnabled = true;
            _Effect.Texture = oGame.Content.Load<Texture2D>(oImagePath);

            FillVertices();
        }

        public Quad(Game oGame, Texture2D oTexture, Rectangle? oRectangle, Vector3 position, Vector3 origin, Vector3 normal, Vector3 up, float width, float height, bool focusOnCamera = true)
        {
            _Position = position;
            _Direction = Vector3.Right;
            _FocusedOnCamera = focusOnCamera;

            Vertices = new VertexPositionNormalTexture[4];
            Indexes = new short[6];
            Origin = origin;
            Normal = normal;
            Up = up;

            // Calculate the quad corners
            Vector3 uppercenter = (Up * height / 2) + origin;

            Left = Vector3.Cross(normal, Up);
            UpperLeft = uppercenter + (Left * width / 2);
            UpperRight = uppercenter - (Left * width / 2);
            LowerLeft = UpperLeft - (Up * height);
            LowerRight = UpperRight - (Up * height);

            _Effect = new BasicEffect(oGame.GraphicsDevice);
            _Effect.EnableDefaultLighting();

            _Effect.World = Matrix.Identity;
            _Effect.TextureEnabled = true;
            _Effect.Texture = oTexture;

            FillVertices(oRectangle);
        }

        private void FillVertices(Rectangle? oRectangle = null)
        {
            Vector2 textureUpperLeft;
            Vector2 textureUpperRight;
            Vector2 textureLowerLeft;
            Vector2 textureLowerRight;

            if (oRectangle == null)
            {
                // Fill in texture coordinates to display full texture on quad
                textureUpperLeft = new Vector2(0.0f, 0.0f);
                textureUpperRight = new Vector2(1.0f, 0.0f);
                textureLowerLeft = new Vector2(0.0f, 1.0f);
                textureLowerRight = new Vector2(1.0f, 1.0f);
            }
            else
            {
                Rectangle r = (Rectangle)oRectangle;
                float tw = _Effect.Texture.Width;
                float th = _Effect.Texture.Height;
                float x1 = ((float)r.Left) / tw;
                float y1 = ((float)r.Top) / th;
                float x2 = ((float)(r.Left + r.Width)) / tw;
                float y2 = ((float)(r.Top + r.Height)) / th;

                textureUpperLeft = new Vector2(x1, y1);
                textureUpperRight = new Vector2(x2, y1);
                textureLowerLeft = new Vector2(x1, y2);
                textureLowerRight = new Vector2(x2, y2);
            }


            // Provide a normal for each vertex
            for (int i = 0; i < Vertices.Length; i++)
            {
                Vertices[i].Normal = Normal;
            }

            // Set the position and texture coordinate for each
            // vertex
            Vertices[0].Position = LowerLeft;
            Vertices[0].TextureCoordinate = textureLowerLeft;
            Vertices[1].Position = UpperLeft;
            Vertices[1].TextureCoordinate = textureUpperLeft;
            Vertices[2].Position = LowerRight;
            Vertices[2].TextureCoordinate = textureLowerRight;
            Vertices[3].Position = UpperRight;
            Vertices[3].TextureCoordinate = textureUpperRight;

            // Set the index buffer for each vertex, using
            // clockwise winding
            Indexes[0] = 3;
            Indexes[1] = 1;
            Indexes[2] = 2;
            Indexes[3] = 2;
            Indexes[4] = 1;
            Indexes[5] = 0;

            _VertexDeclaration = new VertexDeclaration(new VertexElement[]
            {
                new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
                new VertexElement(12, VertexElementFormat.Vector3, VertexElementUsage.Normal, 0),
                new VertexElement(24, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0)
            });
        }

        public void Update(Camera oCamera)
        {
            _Effect.View = oCamera.View;
            _Effect.Projection = oCamera.Projection;

            Vector3 oForward = Vector3.Zero - oCamera.Position;
            if (oForward != Vector3.Zero)
                oForward.Normalize();

            Matrix oTransform = Matrix.CreateLookAt(Vector3.Zero, _Direction, VectorHelper.GLOBAL_UP) 
                * Matrix.CreateTranslation(_Position);

            if (_FocusedOnCamera)
            {
                Vector3 oLookAt = _Position - oCamera.CameraForward;
                oTransform = Matrix.CreateConstrainedBillboard(_Position, oLookAt, VectorHelper.GLOBAL_UP, VectorHelper.GLOBAL_UP, null);
            }

            Vertices[0].Position = Vector3.Transform(LowerLeft, oTransform);
            Vertices[1].Position = Vector3.Transform(UpperLeft, oTransform);
            Vertices[2].Position = Vector3.Transform(LowerRight, oTransform);
            Vertices[3].Position = Vector3.Transform(UpperRight, oTransform);
        }

        public void Draw(Game oGame)
        {
            GraphicsDevice oDevice = oGame.GraphicsDevice;
            //oDevice.RasterizerState = RasterizerState.CullClockwise;
            oDevice.RasterizerState = RasterizerState.CullNone;

            foreach (EffectPass pass in _Effect.CurrentTechnique.Passes)
            {
                pass.Apply();

                oGame.GraphicsDevice.DrawUserIndexedPrimitives
                    <VertexPositionNormalTexture>(
                    PrimitiveType.TriangleList,
                    Vertices, 0, 4,
                    Indexes, 0, 2);
            }
        }

        public BasicEffect Effect
        {
            get { return _Effect; }
            set { _Effect = value; }
        }

        public Vector3 Position
        {
            get { return _Position; }
            set { _Position = value; }
        }

        public Vector3 Direction
        {
            get { return _Direction; }
            set { _Direction = value; }
        }

        BasicEffect _Effect;
        VertexDeclaration _VertexDeclaration;

        Vector3 _Position;
        Vector3 _Direction;
        bool _FocusedOnCamera;

        VertexPositionNormalTexture[] Vertices;
        short[] Indexes;
        Vector3 Origin;
        Vector3 Normal;
        Vector3 Up;

        Vector3 Left;
        Vector3 UpperLeft;
        Vector3 UpperRight;
        Vector3 LowerLeft;
        Vector3 LowerRight;

    }
}
